<?php 

namespace OAuth2\Storage;

class CIDatabase implements AccessTokenInterface, ClientInterface, UserCredentialsInterface, ClientCredentialsInterface, ScopeInterface, RefreshTokenInterface
{
	/**
	 * @var CI_DB_driver
	 */
	protected $db;

	/**
	 * @param array
	 * 表名称配置
     * - access_token_table: access_token表名称.
     * - client_table: client表名称.
     * - user_table: user表名称.
     * - scope_table: scope表名称.
     * - refresh_token_table: refresh_token表名称.
     * - user_identify_field: 用户唯一标示字段
     * - user_primary_key: 用户主键ID
	 */
	protected $config;

	public function __construct(\CI_DB_driver $db, $config = array())
	{
		$this->db = $db;

		$this->config = array_merge(array(
			'access_token_table' => 'access_tokens',
			'client_table' => 'clients',
			'user_table' => 'user',
			'scope_table' => 'scopes',
			'refresh_token_table' => 'refresh_tokens',
			'user_identify_field' => 'phone',
			'user_primary_key' => 'uid',
		), $config);
	}

	//  OAuth2\Storage\ClientInterface
	public function getClientDetails($client_id)
	{
		$result = $this->findOne(array('client_id' => $client_id), $this->config['client_table']);

		return $result ? $result : false;
	}

	public function getClientScope($client_id)
	{
		$result = $this->getClientDetails($client_id);

		return $result ? $result['scope'] : false;
	}

	public function checkRestrictedGrantType($client_id, $grant_type)
	{
		$details = $this->getClientDetails($client_id);
		if (isset($details['grant_types'])) {
            $grant_types = explode(' ', $details['grant_types']);
            return in_array($grant_type, (array) $grant_types);
        }

        return true;
	}

	// OAuth2\Storage\AccessTokenInterface
	public function getAccessToken($oauth_token)
	{
		$result = $this->findOne(array('access_token' => $oauth_token), $this->config['access_token_table']);
		if ($result) {
			$result['expires'] = strtotime($result['expires']);
		}
		return $result ? $result : null;
	}

	public function setAccessToken($oauth_token, $client_id, $user_id, $expires, $scope = null)
	{
		if ($this->getAccessToken($oauth_token)) {
			$result = $this->updateByCondition(array(
				'client_id' => $client_id,
				'user_id' => $user_id,
				'expires' => date('Y-m-d H:i:s', $expires),
				'scope' => $scope
			), array('access_token' => $oauth_token), $this->config['access_token_table']);
		} else {
			$result = $this->db->insert($this->config['access_token_table'], array(
				'access_token' => $oauth_token,
				'client_id' => $client_id,
				'user_id' => $user_id,
				'expires' => date('Y-m-d H:i:s', $expires),
				'scope' => $scope
			));
		}

		return $result;
	}

	public function unsetAccessToken($access_token)
	{
		return $this->db->where('access_token', $access_token)->delete($this->config['access_token_table']);
	}

	// OAuth2\Storage\ClientCredentialsInterface
	public function checkClientCredentials($client_id, $client_secret = null)
	{
		$result = $this->findOne(array('client_id' => $client_id), $this->config['client_table']);

		return $result && $result['client_secret'] == $client_secret;
	}

	public function isPublicClient($client_id)
	{
		$client = $this->findOne(array('client_id' => $client_id), $this->config['client_table']);
		if (!$client) {
			return false;
		}

		return empty($client['client_secret']);
	}

	// OAuth2\Storage\UserCredentialsInterface
	public function checkUserCredentials($username, $password)
	{
		$user = $this->getUser($username);
		if (!$user)
			return false;

		return password_verify($password, $user['password']);
	}

	public function getUserDetails($username)
	{
		return $this->getUser($username);
	}

	/**
	 * 获取用户
	 *
	 * @param string identify
	 */
	public function getUser($username)
	{
		$user = $this->findOne(array($this->config['user_identify_field'] => $username), $this->config['user_table']);
		if ($user) {
			$user['user_id'] = $user[$this->config['user_primary_key']];
		}
		
		return $user;
	}

	// ScopeInterface
	public function scopeExists($scope)
	{
		return $this->findOne(array('scope' => $scope), $this->config['scope_table']) ? true : false;
	}

	public function getDefaultScope($client_id = null)
	{
		$scopeNum = $this->db->count_all_results($this->config['scope_table']);
		if(!$scopeNum)
			return null;
		$result = $this->db->where(array('is_default' => 1))->get($this->config['scope_table'])->result_array();

		$defaultScope = array_map(function ($row) {
            return $row['scope'];
        }, $result);
        
        return implode(' ', $defaultScope);
	}

	// RefreshTokenInterface
	public function getRefreshToken($refresh_token)
	{
		$token = $this->findOne(array('refresh_token' => $refresh_token), $this->config['refresh_token_table']);
		if ($token) {
			$token['expires'] = strtotime($token['expires']);
			return $token;
		}
		
		return null;
	}

	public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null)
	{
		$expires = date('Y-m-d H:i:s', $expires);

		return $this->db->insert($this->config['refresh_token_table'], array(
			'refresh_token' => $refresh_token,
			'client_id' => $client_id,
			'user_id' => $user_id,
			'expires' => $expires,
			'scope' => $scope
		));
	}

	public function unsetRefreshToken($refresh_token)
	{
		return $this->db->where(array('refresh_token' => $refresh_token))->delete($this->config['refresh_token_table']);
	}

	/**
	 * 查询一条记录
	 *
	 * @param array 条件
	 * @param string 表名
	 */
	private function findOne($condition, $table)
	{
		return $this->db->where($condition)->limit(1)->get($table)->row_array();
	}

	/**
	 * 更新记录
	 *
	 * @param array 数据
	 * @param array 条件
	 * @param array 表
	 * @return boolean
	 */
	private function updateByCondition($data, $condition, $table)
	{
		return $this->db->where($condition)->update($table, $data);
	}
}